package simple.utils.tracking;

import com.google.common.base.Preconditions;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import simple.utils.data.Bytes;
import simple.utils.randoms.Randoms;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

/**
 * Created by martin on 28.4.14.
 */
public class TrackingUtils {

    private static final Logger LOGGER = LoggerFactory.getLogger(TrackingUtils.class);
    private static final Randoms r = new Randoms();
    public static final int INTEGER_LENGTH = 4;
    public static final int LONG_LENGTH = 8;
    private static final int RANDOM_BYTES_LEN = 2;
    /**
     * first 4 bytes = key length
     * next X bytes = key
     * last 10 bytes - 8 bytes = timestamp, 2 bytes = random bytes
     * <p/>
     * key length is 14 + size of id itself
     *
     * @param id
     * @param ts
     * @return
     */
    public static byte[] generateID(String id, long ts) {
        // 4 bytes length + id as bytes
        byte[] startBytes = generateStartBytes(id);
        // 8 bytes
        byte[] tsBytes = Bytes.longToBytes(ts);
        // 2 bytes
        byte[] rndBytes = r.randomBytes(RANDOM_BYTES_LEN);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            bos.write(startBytes);
            bos.write(tsBytes);
            bos.write(rndBytes);
            bos.close();
        } catch (IOException e) {
            LOGGER.error("Unlikely error writing to ByteArrayOutputStream or closing it!", e);
        }
//        LOGGER.info(Arrays.toString(bos.toByteArray()));
        return bos.toByteArray();
    }

    public static boolean keyMatches(byte[] key, String id) {
        byte[] bytes = generateStartBytes(id);
        if (bytes.length >= key.length) {
            return false;
        }
        for (int i = 0; i < bytes.length; i++) {
            if (bytes[i] != key[i]) {
                return false;
            }
        }
        return true;
    }

    public static boolean keyMatchesRange(byte[] key, String id, long end) {
        byte[] bytes = generateStartBytes(id);
        if (bytes.length >= key.length) {
            return false;
        }
        for (int i = 0; i < bytes.length; i++) {
            if (bytes[i] != key[i]) {
                return false;
            }
        }

        long ts = Bytes.bytesToLong(Arrays.copyOfRange(key, bytes.length, bytes.length + LONG_LENGTH));
        return new DateTime(ts).isBefore(end);
    }

    public static byte[] generateStartBytes(String id) {
        Preconditions.checkNotNull(id);
        byte[] idLengthBytes = Bytes.intToBytes(id.length());
        byte[] idBytes = id.getBytes(StandardCharsets.UTF_8);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            bos.write(idLengthBytes);
            bos.write(idBytes);
            bos.close();
        } catch (IOException e) {
            throw new RuntimeException("Fatal error!", e);
        }
        return bos.toByteArray();
    }

    public static byte[] idPart(byte [] bytes) {
        int len = Bytes.bytesToInt(Arrays.copyOf(bytes, INTEGER_LENGTH));
        return Arrays.copyOf(bytes, INTEGER_LENGTH + len);
    }

    public static byte [] getRandonBytes(int num) {
        return r.randomBytes(num);
    }
    public static byte [] getRandonBytes() {
        return getRandonBytes(RANDOM_BYTES_LEN);
    }
}
